//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.anji.plus.gaea.curd.service;

import com.anji.plus.gaea.annotation.*;
import com.anji.plus.gaea.cache.GaeaCacheService;
import com.anji.plus.gaea.constant.BaseOperationEnum;
import com.anji.plus.gaea.curd.entity.BaseEntity;
import com.anji.plus.gaea.curd.entity.GaeaBaseEntity;
import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
import com.anji.plus.gaea.curd.params.PageParam;
import com.anji.plus.gaea.exception.BusinessException;
import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
import com.anji.plus.gaea.utils.*;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.springframework.transaction.annotation.Transactional;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

public interface GaeaBaseService<P extends PageParam, T extends BaseEntity> {
    GaeaBaseMapper<T> getMapper();

    default GaeaCacheService getCacheService() {
        return (GaeaCacheService) ApplicationContextUtils.getBean(GaeaCacheService.class);
    }

    default FieldQueryDialect getFieldQueryDialect() {
        return (FieldQueryDialect) ApplicationContextUtils.getBean(FieldQueryDialect.class);
    }

    default T wrapperEntity(T entity) {
        return entity;
    }

    default T selectOne(String id) {
        T t = this.getMapper().selectById(id);
        return this.wrapperEntity(t);
    }

    default T selectOne(String id, boolean checkExist) {
        T t = this.getMapper().selectById(id);
        if (checkExist) {
            GaeaAssert.notNull(t, "500-0001", new Object[0]);
        }

        return this.wrapperEntity(t);
    }

    default T selectOne(Wrapper<T> wrapper) {
        return this.wrapperEntity(this.getMapper().selectOne(wrapper));
    }

    default T selectOne(String column, Object value) {
        //处理双引号
        if (!StringUtils.isEmpty(column) && !column.contains("\"")) {
            column = "\"" + column + "\"";
        }
        List<T> list = this.list(column, value);
        return list.isEmpty() ? null : this.wrapperEntity(list.get(0));
    }

    default IPage<T> page(P pageParam) {
        return this.page(pageParam, (Wrapper) null);
    }

    default OrderItem build(String column, String order) {
        OrderItem item = new OrderItem();
        item.setColumn(column);
        if ("ASC".equalsIgnoreCase(order)) {
            item.setAsc(true);
        } else {
            item.setAsc(false);
        }

        return item;
    }

    default IPage<T> page(P pageParam, Wrapper<T> wrapper) {
        Page<T> page = new Page();
        page.setCurrent((long) pageParam.getPageNumber());
        page.setSize((long) pageParam.getPageSize());
        String sort = pageParam.getSort();
        //排序字段处理
        if (!StringUtils.isEmpty(sort) && !sort.contains("\"")) {
            sort = "\"" + sort + "\"";
        }
        String order = pageParam.getOrder();
        String[] sortSplit = new String[0];
        String[] orderSplit = new String[0];
        if (StringUtils.isNotBlank(sort) && StringUtils.isNotBlank(order)) {
            if (sort.contains(",") && order.contains(",")) {
                sortSplit = sort.split(",");
                orderSplit = order.split(",");
                if (orderSplit.length == sortSplit.length) {
                    OrderItem[] orderItems = new OrderItem[orderSplit.length];

                    for (int i = 0; i < sortSplit.length; ++i) {
                        orderItems[i] = this.build(GaeaUtils.camelToUnderline(sortSplit[i]), orderSplit[i]);
                    }

                    page.addOrder(orderItems);
                } else {
                    String order0 = orderSplit[0];
                    OrderItem[] orderItems = new OrderItem[orderSplit.length];

                    for (int i = 0; i < sortSplit.length; ++i) {
                        orderItems[i] = this.build(GaeaUtils.camelToUnderline(sortSplit[i]), order0);
                    }

                    page.addOrder(orderItems);
                }
            }

            if (sort.contains(",") && !order.contains(",")) {
                List<String> orderList = (List) Arrays.stream(sort.split(",")).map(GaeaUtils::camelToUnderline).collect(Collectors.toList());
                String[] orderColumns = (String[]) orderList.toArray(new String[0]);
                if ("ASC".equalsIgnoreCase(order)) {
                    page.addOrder(OrderItem.ascs(orderColumns));
                } else {
                    page.addOrder(OrderItem.descs(orderColumns));
                }
            }

            if (!sort.contains(",") && !order.contains(",")) {
                page.addOrder(new OrderItem[]{this.build(GaeaUtils.camelToUnderline(sort), order)});
            }
        }

        if (wrapper != null) {
            IPage<T> ret = this.getMapper().selectPage(page, wrapper);
            pageParam.setTotal(ret.getTotal());
            return this.resultHandler(ret);
        } else {
            Wrapper<T> pageWrapper = this.extensionWrapper(pageParam, this.getWrapper(pageParam));
            this.handlerPageWrapper(pageWrapper);
            IPage<T> ret = this.getMapper().selectPage(page, pageWrapper);
            pageParam.setTotal(ret.getTotal());
            return this.resultHandler(ret);
        }
    }

    default Wrapper<T> handlerPageWrapper(Wrapper<T> pageWrapper) {
        return pageWrapper;
    }

    /**
     * @deprecated
     */
    default IPage<T> resultHandler(IPage<T> iPage) {
        return iPage;
    }

    default IPage<T> resultHandler(IPage<T> iPage, P param) {
        return iPage;
    }

    default Wrapper<T> extensionWrapper(P param, QueryWrapper<T> wrapper) {
        return wrapper;
    }

    default void initWrapper(P param, QueryWrapper<T> queryWrapper) {
        Field[] fields = param.getClass().getDeclaredFields();
        Arrays.stream(fields).filter((field) -> {
            if (field.isAnnotationPresent(Query.class)) {
                Query query = (Query) field.getAnnotation(Query.class);
                return query.where();
            } else {
                return true;
            }
        }).forEach((field) -> {
            try {
                field.setAccessible(true);
                Object value = field.get(param);
                String column;
                if (field.isAnnotationPresent(Query.class) && StringUtils.isNotBlank(((Query) field.getAnnotation(Query.class)).column())) {
                    column = ((Query) field.getAnnotation(Query.class)).column();
                } else {
                    column = GaeaUtils.camelToUnderline(field.getName());
                }
                //oracle处理双引号
                if (!StringUtils.isEmpty(column) && !column.contains("\"")) {
                    column = "\"" + column + "\"";
                }


                boolean flag;
                if (value instanceof String) {
                    flag = StringUtils.isNoneBlank(new CharSequence[]{(String) value});
                } else {
                    flag = value != null;
                }

                if (!flag) {
                    return;
                }

                if (field.isAnnotationPresent(Query.class)) {
                    String[] split;
                    switch (((Query) field.getAnnotation(Query.class)).value()) {
                        case LIKE:
                            String likeValue = String.valueOf(value);
                            if (likeValue.contains("%")) {
                                likeValue = likeValue.replace("%", "\\%");
                            }

                            if (likeValue.contains("_")) {
                                likeValue = likeValue.replace("_", "\\_");
                            }

                            queryWrapper.like(column, likeValue);
                            break;
                        case MATCH:
                            this.getFieldQueryDialect().match(queryWrapper, param, column, value);
                            break;
                        case IN:
                            if (value instanceof List) {
                                queryWrapper.in(column, (List) value);
                            } else if (value instanceof String) {
                                split = ((String) value).split(",");
                                List<String> list = Arrays.asList(split);
                                queryWrapper.in(column, list);
                            }
                            break;
                        case GT:
                            queryWrapper.gt(column, value);
                            break;
                        case GE:
                            queryWrapper.ge(column, value);
                            break;
                        case LT:
                            queryWrapper.lt(column, value);
                            break;
                        case LE:
                            queryWrapper.le(column, value);
                            break;
                        case BWT:
                            split = value.toString().split(",");
                            if (split.length == 2) {
                                if (GaeaDateUtils.checkTimeFormat(split[0])) {
                                }

                                queryWrapper.between(column, GaeaDateUtils.sourceTimeZoneToTargetTimeZone(split[0]), GaeaDateUtils.sourceTimeZoneToTargetTimeZone(split[1]));
                            } else if (split.length == 1) {
                                queryWrapper.ge(column, GaeaDateUtils.sourceTimeZoneToTargetTimeZone(split[0]));
                            }
                            break;
                        default:
                            queryWrapper.eq(column, value);
                    }
                } else {
                    queryWrapper.eq(column, value);
                }
            } catch (IllegalAccessException var10) {
                IllegalAccessException e = var10;
                e.printStackTrace();
            }

        });
    }

    default QueryWrapper<T> getWrapper(P param) {
        QueryWrapper<T> queryWrapper = new QueryWrapper();
        this.initWrapper(param, queryWrapper);
        return queryWrapper;
    }

    default void processBeforeOperation(T entity, BaseOperationEnum opr) throws BusinessException {
    }

    default void processAfterOperation(T entity, BaseOperationEnum opr) throws BusinessException {
    }

    default String getColumn(SFunction<T, ?> function) {
        LambdaMeta lambda = LambdaUtils.extract(function);
        String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
        Class<?> implClass = lambda.getInstantiatedClass();

        try {
            Field field = implClass.getDeclaredField(fieldName);
            if (field.isAnnotationPresent(TableField.class)) {
                fieldName = ((TableField) field.getAnnotation(TableField.class)).value();
                return fieldName;
            }
        } catch (NoSuchFieldException var6) {
        }

        return GaeaUtils.camelToUnderline(fieldName);
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    default Integer insert(T entity) throws BusinessException {
        this.processBeforeOperation(entity, BaseOperationEnum.INSERT);
        this.checkUniqueField(entity, false);
        Integer result = this.getMapper().insert(entity);
        if (result != null && result >= 1) {
            this.refreshCacheFields(entity, BaseOperationEnum.INSERT);
            this.processAfterOperation(entity, BaseOperationEnum.INSERT);
            return result;
        } else {
            throw BusinessExceptionBuilder.build("Insert.failure");
        }
    }

    default void refreshCacheFields(T entity, BaseOperationEnum opr) {
        this.getCacheService().refreshCacheFields(entity, opr);
    }

    default void refreshCacheFieldsBatch(List<T> entities, BaseOperationEnum opr) {
        this.getCacheService().refreshCacheFieldsBatch(entities, opr);
    }

    default String formatKey(String key, String[] replaceArray, T entity) {
        return GaeaUtils.formatKey(key, replaceArray, entity);
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    default Integer insertBatch(List<T> entities) throws BusinessException {
        Integer result = this.getMapper().insertBatch(entities);
        if (result != null && result >= 1) {
            this.getCacheService().refreshCacheFieldsBatch(entities, BaseOperationEnum.INSERT);
            return result;
        } else {
            throw BusinessExceptionBuilder.build("Insert.failure");
        }
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    default Integer update(T entity) throws BusinessException {
        this.processBeforeOperation(entity, BaseOperationEnum.UPDATE);
        this.checkUniqueField(entity, true);
        if (entity instanceof GaeaBaseEntity) {
            GaeaBaseEntity gaeaBaseEntity = (GaeaBaseEntity) entity;
            T dbEntity = this.getById(gaeaBaseEntity.getId());
            this.refreshCacheFields(dbEntity, BaseOperationEnum.DELETE);
        }

        Integer result = this.getMapper().updateById(entity);
        if (result != null && result >= 1) {
            this.refreshCacheFields(entity, BaseOperationEnum.UPDATE);
            this.processAfterOperation(entity, BaseOperationEnum.UPDATE);
            return result;
        } else {
            throw BusinessExceptionBuilder.build("Update.failure");
        }
    }

    default Integer updateFieldsById(Map<String, Object> map, Long id) {
        map.computeIfAbsent("update_time", (key) -> {
            return new Date();
        });
        return this.getMapper().updateFieldsById(map, id);
    }

    default Integer updateBatchFieldsById(Map<String, Object> map, List<Long> ids) {
        map.computeIfAbsent("update_time", (key) -> {
            return new Date();
        });
        return this.getMapper().updateFieldsBatchById(map, ids);
    }

    default Integer updateBatchFields(Map<String, Object> map, List<T> list) {
        map.computeIfAbsent("update_time", (key) -> {
            return new Date();
        });
        return this.getMapper().updateFieldsBatch(map, list);
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    default Integer deleteById(Serializable id) {
        T t = this.getById(id);
        if (t == null) {
            throw BusinessExceptionBuilder.build("500-0001");
        } else {
            this.processBeforeOperation(t, BaseOperationEnum.DELETE);
            Integer result = this.getMapper().deleteById(id);
            if (result != null && result >= 1) {
                this.refreshCacheFields(t, BaseOperationEnum.DELETE);
                this.processAfterOperation(t, BaseOperationEnum.DELETE);
                return result;
            } else {
                throw BusinessExceptionBuilder.build("Delete.failure");
            }
        }
    }

    default void processBatchBeforeOperation(List<T> entities, BaseOperationEnum opr) throws BusinessException {
    }

    default void processBatchAfterOperation(List<T> entities, BaseOperationEnum opr) throws BusinessException {
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    default boolean deleteByIds(Collection<? extends Serializable> idList) {
        List<T> list = this.getMapper().selectBatchIds(idList);
        this.processBatchBeforeOperation(list, BaseOperationEnum.DELETE_BATCH);
        boolean result = SqlHelper.retBool(this.getMapper().deleteBatchIds(idList));
        if (result) {
            this.getCacheService().refreshCacheFieldsBatch(list, BaseOperationEnum.DELETE_BATCH);
            this.processBatchAfterOperation(list, BaseOperationEnum.DELETE_BATCH);
        }

        return result;
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    default void delete(LambdaQueryWrapper<T> lambdaQueryWrapper) {
        List<T> list = this.getMapper().selectList(lambdaQueryWrapper);
        if (!CollectionUtils.isEmpty(list)) {
            this.deleteByIds((Collection) list.stream().map((d) -> {
                return d instanceof GaeaBaseEntity ? ((GaeaBaseEntity) d).getId() : 0L;
            }).collect(Collectors.toList()));
        }
    }

    default void checkUniqueField(T entity, boolean isUpdate) {
        Field[] allFields = ReflectionUtils.getAllFieldsArr(entity);
        Optional<Field> idFiledOptional = Arrays.stream(allFields).filter((fieldx) -> {
            return fieldx.isAnnotationPresent(TableId.class);
        }).findFirst();
        if (idFiledOptional.isPresent()) {
            Field idField = (Field) idFiledOptional.get();
            idField.setAccessible(true);
            Field[] var6 = allFields;
            int var7 = allFields.length;

            int var8;
            for (var8 = 0; var8 < var7; ++var8) {
                Field field = var6[var8];
                if (field.isAnnotationPresent(Unique.class)) {
                    Unique unique = (Unique) field.getDeclaredAnnotation(Unique.class);
                    QueryWrapper<T> wrapper = Wrappers.query();

                    Long integer;
                    try {
                        Object value = this.getFieldValue(entity, field);
                        String column;
                        if (StringUtils.isBlank(unique.column())) {
                            column = GaeaUtils.camelToUnderline(field.getName());
                        } else {
                            column = unique.column();
                        }

                        wrapper.eq(column, value);
                        if (isUpdate) {
                            wrapper.ne(((TableId) idField.getAnnotation(TableId.class)).value(), idField.get(entity));
                        }

                        integer = this.getMapper().selectCount(wrapper);
                    } catch (Exception var23) {
                        continue;
                    }

                    if (integer > 0L) {
                        throw BusinessExceptionBuilder.build(unique.code(), new Object[]{field.getName()});
                    }
                }
            }

            Map<String, QueryWrapper<T>> unionUniqueMap = new HashMap();
            Field[] var25 = allFields;
            var8 = allFields.length;

            for (int var28 = 0; var28 < var8; ++var28) {
                Field field = var25[var28];
                List<UnionUnique> unionUniques = null;
                Object value = null;
                if (field.isAnnotationPresent(UnionUnique.class)) {
                    if (unionUniques == null) {
                        unionUniques = new ArrayList();
                    }

                    try {
                        value = this.getFieldValue(entity, field);
                        UnionUnique[] ret = (UnionUnique[]) field.getDeclaredAnnotationsByType(UnionUnique.class);
                        unionUniques.addAll((Collection) Arrays.stream(ret).collect(Collectors.toList()));
                    } catch (Exception var21) {
                        continue;
                    }
                }

                if (field.isAnnotationPresent(UnionUniques.class)) {
                    if (unionUniques == null) {
                        unionUniques = new ArrayList();
                    }

                    try {
                        value = this.getFieldValue(entity, field);
                        UnionUniques[] ret1 = (UnionUniques[]) field.getDeclaredAnnotationsByType(UnionUniques.class);
                        UnionUniques[] var39 = ret1;
                        int var15 = ret1.length;

                        for (int var16 = 0; var16 < var15; ++var16) {
                            UnionUniques gs = var39[var16];
                            unionUniques.addAll((Collection) Arrays.stream(gs.value()).collect(Collectors.toList()));
                        }
                    } catch (Exception var22) {
                        continue;
                    }
                }

                if (CollectionUtils.isNotEmpty(unionUniques)) {
                    Iterator var37 = unionUniques.iterator();

                    while (var37.hasNext()) {
                        UnionUnique unionUnique = (UnionUnique) var37.next();
                        String group = unionUnique.group();
                        String column;
                        if (StringUtils.isBlank(unionUnique.column())) {
                            column = GaeaUtils.camelToUnderline(field.getName());
                        } else {
                            column = unionUnique.column();
                        }

                        QueryWrapper unionWrapper;
                        if (unionUniqueMap.containsKey(group)) {
                            unionWrapper = (QueryWrapper) unionUniqueMap.get(group);
                            unionWrapper.eq(column, value);
                        } else {
                            unionWrapper = Wrappers.query();
                            unionWrapper.eq(column, value);
                            unionUniqueMap.put(group, unionWrapper);
                        }
                    }
                }
            }

            Set<Map.Entry<String, QueryWrapper<T>>> entries = unionUniqueMap.entrySet();
            Iterator var27 = entries.iterator();

            while (true) {
                Map.Entry entry;
                long result;
                do {
                    if (!var27.hasNext()) {
                        return;
                    }

                    entry = (Map.Entry) var27.next();
                    QueryWrapper<T> queryWrapper = (QueryWrapper) entry.getValue();
                    if (isUpdate) {
                        try {
                            queryWrapper.ne(((TableId) idField.getAnnotation(TableId.class)).value(), idField.get(entity));
                        } catch (Exception var20) {
                            return;
                        }
                    }

                    result = this.getMapper().selectCount(queryWrapper);
                } while (result <= 0L);

                String group = (String) entry.getKey();
                Class<? extends BaseEntity> aClass = entity.getClass();
                UnionUniqueCode[] unionUniqueCodes = (UnionUniqueCode[]) aClass.getAnnotationsByType(UnionUniqueCode.class);
                UnionUniqueCode[] var46 = unionUniqueCodes;
                int var47 = unionUniqueCodes.length;

                for (int var18 = 0; var18 < var47; ++var18) {
                    UnionUniqueCode unionUniqueCode = var46[var18];
                    if (StringUtils.equals(unionUniqueCode.group(), group)) {
                        throw BusinessExceptionBuilder.build(unionUniqueCode.code());
                    }
                }
            }
        }
    }

    default Object getFieldValue(T entity, Field field) throws IntrospectionException, IllegalAccessException, InvocationTargetException {
        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), entity.getClass());
        Method readMethod = propertyDescriptor.getReadMethod();
        return readMethod.invoke(entity);
    }

    default List<T> list(String column, Object value) {
        QueryWrapper<T> queryWrapper = new QueryWrapper();
        queryWrapper.eq(column, value);
        return this.getMapper().selectList(queryWrapper);
    }

    default Long count(Wrapper<T> wrapper) {
        return this.getMapper().selectCount(wrapper);
    }

    default List<T> list(Wrapper<T> wrapper) {
        return this.getMapper().selectList(wrapper);
    }

    default T getById(Serializable id) {
        return this.getMapper().selectById(id);
    }

    default List<T> findAll() {
        return this.getMapper().selectList(Wrappers.emptyWrapper());
    }

    default Integer updateColumn(Long id, String column, Object value) {
        Map<String, Object> params = new HashMap(1);
        params.put(column, value);
        params.put("update_time", new Date());
        return this.getMapper().updateFieldsById(params, id);
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    default Integer updateFieldsBatch(Map<String, Object> map, List<T> list) {
        map.computeIfAbsent("update_time", (key) -> {
            return new Date();
        });
        return this.getMapper().updateFieldsBatch(map, list);
    }
}
